#ifndef __RT_FUTURE2_H
#define __RT_FUTURE2_H

#include "./result2.h"
#include <coroutine>
#include <memory>

namespace rt {

template <typename T> class Notify2 {

public:
    void complete(Result2<T> &&value) {
        m_value = std::move(value);
        resume();
    }

    template <typename U = T>
    std::enable_if_t<std::is_void_v<U>, void> complete() {
        m_value.set_value();
        resume();
    }

    template <typename U = T>
    std::enable_if_t<!std::is_void_v<U>, void> complete(typename Result2<T>::TYPE &&value) {
        m_value.set_value(std::move(value));
        resume();
    }

    void complete(const std::exception_ptr &ptr) {
        m_value = ptr;
        resume();
    }

    bool set_handle(std::coroutine_handle<> handle) {
        m_handle = handle;
        return m_value;
    }

    void reset() { m_value = Result2<T>::None(); }

    T get() {
        if constexpr (std::is_void_v<T>) {
            m_value.get();
        } else {
            return m_value.get();
        }
    }

    bool is_complete() const noexcept { return m_value; }

protected:
    void resume() {
        std::coroutine_handle<> h = m_handle;
        m_handle = nullptr;
        if (h) {
            h.resume();
        }
    }

    Result2<T> m_value;
    std::coroutine_handle<> m_handle;
};

template <typename T = void> class CompletabledFuture2 {

public:
    void reset() { m_notify->reset(); }

    std::weak_ptr<Notify2<T>> weak() { return m_notify; }

    bool await_ready() const noexcept { return m_notify->is_complete(); }

    bool await_suspend(std::coroutine_handle<> h) {
        return !m_notify->set_handle(h);
    }

    T await_resume() {
        if constexpr (std::is_void_v<T>) {
            m_notify->get();
        } else {
            return m_notify->get();
        }
    }

private:
    std::shared_ptr<Notify2<T>> m_notify = std::make_shared<Notify2<T>>();
};

} // namespace rt

#endif
